﻿using UnityEngine;
using UnityEngine.UI;

namespace CGF
{
    [AddComponentMenu("CGFramework/CUIParticle")]
    [RequireComponent(typeof(RectTransform))]
    public class CUIParticle : MonoBehaviour
    {
        class Line
        {
            public float k, b;
            public Vector2 p1, p2;
            public float radian;
            public Line(float x1, float y1, float x2, float y2)
            {
                this.p1 = new Vector2(x1, y1);
                this.p2 = new Vector2(x2, y2);
                k = (y2 - y1) / (x2 - x1);
                b = y1 - k * x1;
                radian = Mathf.Atan2(y2 - y1, x2 - x1);
            }
        }

        public bool isEditMode = true;
        public bool playOnAwake = true;
        public bool loop = true;
        public int count = 20;
        public float lifeTimeMin = 0.2f, lifeTimeMax = 0.5f;
        public float startSpeedMin = 0f, startSpeedMax = 50f;
        public float verticalForce = 0;
        public float gravity = 3;
        public int startAngleMin = 0, startAngleMax = 360;
        public float angleAddMin, angleAddMax;
        public float startScaleMin = 1f, startScaleMax = 1f;
        public float scaleAddMin, scaleAddMax;
        public Color[] startColors = new Color[] { Color.white };
        public Color[] endColors = new Color[] { Color.white };
        public int spritesFps = 8;
        public bool spritesRandomStart = true;
        public Sprite[] sprites;
        public Material material;

        Item[] items = null;
        int currCount;

        class Item
        {
            public Image image;
            public RectTransform trans;
            float radian, speed, verticalForce, gv, angle, angleAdd, timer;
            Vector3 scaleAdd;
            CUIParticle particle;
            Line colorKB;
            bool active;
            float interval, spritesTimer;
            int frame;
            Sprite[] sprites;
            Color startColor, endColor;
            public Item(CUIParticle particle)
            {
                this.particle = particle;
                this.sprites = particle.sprites;
                this.interval = 1 / (float)particle.spritesFps;
                this.frame = particle.spritesRandomStart ? Random.Range(0, sprites.Length) : 0;
            }
            public void Show()
            {
                if (!trans.gameObject.activeInHierarchy)
                {
                    trans.gameObject.SetActive(true);
                }
            }
            public void Play()
            {
                trans.anchoredPosition = Vector3.zero;
                trans.localScale = Vector3.one * Random.Range(particle.startScaleMin, particle.startScaleMax);
                scaleAdd = Vector3.one * Random.Range(particle.scaleAddMin, particle.scaleAddMax);
                radian = Random.Range(0f, Mathf.PI * 2f);
                speed = Random.Range(particle.startSpeedMin, particle.startSpeedMax);
                verticalForce = particle.verticalForce;
                gv = 0;
                timer = Random.Range(particle.lifeTimeMin, particle.lifeTimeMax);
                angle = Random.Range(particle.startAngleMin, particle.startAngleMax);
                trans.localEulerAngles = new Vector3(0, 0, angle);
                angleAdd = Random.Range(particle.angleAddMin, particle.angleAddMax);
                int colorIndex = 0;
                if (particle.startColors.Length == 1)
                {
                    image.color = startColor = particle.startColors[0];
                }
                else if (particle.startColors.Length > 1)
                {
                    colorIndex = Random.Range(0, particle.startColors.Length);
                    image.color = startColor = particle.startColors[colorIndex];
                }
                if (particle.endColors.Length == 1)
                {
                    endColor = particle.endColors[0];
                }
                else if (particle.endColors.Length > colorIndex)
                {
                    endColor = particle.endColors[colorIndex];
                }
                image.raycastTarget = false;
                colorKB = new Line(timer, 0f, 0f, 1f);
                active = true;
            }
            public void Update()
            {
                if (active)
                {
                    float deltaTime = Time.deltaTime * 30f;
                    Vector3 pos = trans.anchoredPosition;
                    pos.x += Mathf.Cos(radian) * speed * deltaTime;
                    pos.y += Mathf.Sin(radian) * speed * deltaTime;
                    pos.y += verticalForce;
                    pos.y -= gv * deltaTime;
                    gv += particle.gravity * deltaTime;
                    trans.anchoredPosition = pos;
                    trans.localScale += scaleAdd * deltaTime;
                    Vector3 angle = trans.localEulerAngles;
                    angle.z += angleAdd * deltaTime;
                    trans.localEulerAngles = angle;
                    image.color = Color.Lerp(startColor, endColor, colorKB.k * timer + colorKB.b);
                    if (timer > 0f)
                    {
                        timer -= Time.deltaTime;
                        if (timer <= 0f)
                        {
                            if (particle.loop)
                            {
                                Play();
                            }
                            else
                            {
                                active = false;
                                trans.gameObject.SetActive(false);
                            }
                        }
                    }

                    //逐帧播放
                    if (sprites.Length > 1)
                    {
                        spritesTimer += Time.deltaTime;
                        if (spritesTimer >= interval)
                        {
                            spritesTimer = 0;
                            frame++;
                            if (frame > sprites.Length)
                            {
                                frame = 1;
                            }
                            image.sprite = sprites[frame - 1];
                        }
                    }
                }
            }
        }

        void OnEnable()
        {
            if (playOnAwake)
            {
                Play();
            }
        }

        void OnDisable()
        {
            if (Application.isEditor && isEditMode)
            {
                for (int i = 0; i < transform.childCount; i++)
                {
                    Destroy(transform.GetChild(i).gameObject);
                }
                items = null;
            }
        }

        public void Play()
        {
            if (items == null && sprites.Length > 0)
            {
                this.currCount = count;
                items = new Item[currCount];
                for (int i = 0; i < currCount; i++)
                {
                    items[i] = new Item(this);
                    items[i].trans = new GameObject("item", typeof(RectTransform)).GetComponent<RectTransform>();
                    items[i].trans.gameObject.AddComponent<CanvasRenderer>();
                    items[i].image = items[i].trans.gameObject.AddComponent<Image>();
                    if (material)
                    {
                        items[i].image.material = material;
                    }
                    else
                    {
                        items[i].image.sprite = sprites[0];
                    }
                    items[i].trans.SetParent(transform, false);
                }
            }
            for (int i = 0; i < currCount; i++)
            {
                items[i].Show();
                items[i].Play();
            }
        }

        void Update()
        {
            if (items != null)
            {
                for (int i = 0; i < currCount; i++)
                {
                    items[i]?.Update();
                }
            }
        }

        void DestroyAllItems()
        {
            for (int i = 0; i < items.Length; i++)
            {
                if (items[i] != null && items[i].trans)
                {
                    Destroy(items[i].trans.gameObject);
                }
            }
        }

#if UNITY_EDITOR
        [UnityEditor.MenuItem("CGFramework/创建 CUIParticle")]
        [UnityEditor.MenuItem("GameObject/CGFramework/创建 CUIParticle", priority = -2200)]
        static void Create()
        {
            GameObject go = new GameObject("CUIParticle");
            go.layer = LayerMask.NameToLayer("UI");
            go.transform.SetParent(UnityEditor.Selection.activeGameObject.transform, false);
            go.AddComponent<CUIParticle>();
            UnityEditor.EditorUtility.SetDirty(go);
        }
#endif
    }

}